/**
 * DrawBubble.java
 * 
 * @authors: Connor Gordon, Difan Zhao, Kathleen Lan, Patrick Royal
 */

package view.visualizer;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.List;
import model.Element;
import view.visualComponent.Bubble;


/**
 * This class draws a bubble chart - a type of graph which visualizes the
 * relative size of data in a set. Each data value is represented
 * by a circle, or bubble, and all of the bubbles are placed non-overlappingly
 * inside a supercircle, or superbubble. The area of each
 * bubble is approximately the proportion of the corresponding data value's
 * magnitude relative to the total sum of all data values.
 * 
 * @author difan zhao, kathleen lan
 * 
 */
public class BubbleDrawer extends ElementVisualDrawer {

    private static final long serialVersionUID = 1L;
    private static final int DTHETA = 5;
    private static final double DR_PERCENT_OF_MAX_RADIUS = 0.01;
    private static final double OFFSET_PERCENT = 0.80;
    private static final double MAX_THETA = 2 * Math.PI;
    private static final double MIN_THETA = 0;
    private List<Bubble> myBubbles;
    //private static BubbleDrawer myBubbleDrawer;
    private int myCanvasWidth;
    private int myCanvasHeight;
    // the following variables are set to 0 by default but are given other
    // values when the Canvas size is set
    private int myXCenterOfCanvas = 0;
    private int myYCenterOfCanvas = 0;
    private int myMaxRadius = 0;
    private int myCenterRadius = 0;
    private double myDTheta = Math.toRadians(DTHETA);
    private int myDR = DTHETA;
    private boolean myReadyToPaint = false;

    /**
     * Constructor.
     */
    public BubbleDrawer () {
        myBubbles = new ArrayList<Bubble>();
    }

    /**
     * Check which bubble was clicked.
     * @param x x-coordinate of click
     * @param y y-coordinate of click
     */
    public String clickedElementVisualCheck (int x, int y) {
        for (Bubble b : myBubbles) {
            String stockName = b.clickedOn(x, y);
            if (stockName != null) {
                return stockName;
            }
        }
        return null;
    }

    /**
     * Paints the Bubble chart.
     * @param g graphics object
     */
    public void paintComponent (Graphics g) {
        Graphics2D g2 = (Graphics2D)g;
        if (myReadyToPaint) {
            for (Bubble circle : myBubbles) {
                System.out.println("test");
                circle.paint(g2);
            }
        }
    }

    /**
     * Iterates through elementList and makes a new Bubble for each Element.
     * @param elementList data through which to iterate
     */
    public void createVisualization (List<Element> elementList) {
        List<Element> myElements = elementList;
        double sumValue = 0;
        for (Element e : myElements) {
            sumValue += e.getValue();
        }
        for (Element e : myElements) {
            calculateElementVisualPosition(e.getValue() / sumValue,
                    e.getName(), e.getID());
        }
        myReadyToPaint = !myReadyToPaint;
        repaint();
    }

    /**
     * Calculates appropriate radius for new bubble.
     * 
     * @param p proportion of Element's value compared to total sum of values of
     *        all Elements in the same dataset
     * @return
     */
    private int calculateRadiusOfNewBubble (double p) {
        Double rTemp = OFFSET_PERCENT * myMaxRadius * Math.sqrt(p);
        return rTemp.intValue();
    }

    /**
     * calculate the position and color of the bubble
     * 
     * @param relativeSize proportion of Element's value compared to total sum
     *        of values of all Elements in the same dataset
     * @param name name of the element
     * @param label label of the element
     */
    public void calculateElementVisualPosition (double relativeSize,
            String name, String label) {
        int myRadius = calculateRadiusOfNewBubble(relativeSize);
        if (myBubbles.isEmpty()) {
            placeFirstBubble(myRadius, name, label);
        }
        else {
            // n = 2, 3, 4, ...
            placeNthBubble(myRadius, name, label);
        }
    }

    /**
     * Places first bubble in the center of the canvas.
     * 
     * @param r radius of first bubble (bubble to be placed)
     */
    private void placeFirstBubble (int r, String name, String label) {
        makeElementVisual(myXCenterOfCanvas, myYCenterOfCanvas, r, r, name, label);
        myCenterRadius = r;
    }

    /**
     * Places nth bubble (n = 2, 3, 4, ...).
     * Currently, this is a crude calculation/algorithm. To be improved.
     * 
     * @param r radius of bubble to be placed
     */
    private void placeNthBubble (int r, String name, String label) {
        double theta = MIN_THETA;
        int radiusFromGraphCenter = myCenterRadius + r;
        Double xTestPosition;
        Double yTestPosition;
        while (radiusFromGraphCenter <= myMaxRadius) {
            xTestPosition = calculatePotentialXPosition(theta,
                    radiusFromGraphCenter);
            yTestPosition = calculatePotentialYPosition(theta,
                    radiusFromGraphCenter);
            if (!bubbleOverlaps(xTestPosition.intValue(),
                    yTestPosition.intValue(), r)) {
                makeElementVisual(xTestPosition.intValue(),
                        yTestPosition.intValue(), r, r, name, label);
                return;
            }
            if (theta + myDTheta >= MAX_THETA) {
                theta = theta - MAX_THETA;
                radiusFromGraphCenter += myDR;
            }
            theta += myDTheta;
        }
    }

    /**
     * Checks whether a (hypothetical/not yet created) Bubble would overlap with
     * any existing Bubbles.
     * 
     * @param x x-position of Bubble to be compared with existing Bubbles
     * @param y y-position of Bubble to be compared with existing Bubbles
     * @param r radius of Bubble to be compared with existing Bubbles
     */
    private boolean bubbleOverlaps (int x, int y, int r) {
        for (Bubble circle : myBubbles) {
            if (circle.intersectsWithElementVisual(x, y, r, r)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Calculates the x-coordinate of the test position for given theta and
     * radius rTotal.
     * 
     * @param theta angle of vector between center of canvas and test position
     * @param rTotal distance from test position to center of canvas,
     *        (xCenterOfCanvas, yCenterOfCanvas)
     */
    private double calculatePotentialXPosition (double theta, int rTotal) {
        return myXCenterOfCanvas + rTotal * Math.cos(theta);
    }

    /**
     * Calculates the y-coordinate of the test position for given theta and
     * radius rTotal.
     * 
     * @param theta angle of vector between center of canvas and test position
     * @param rTotal distance from test position to the center of the canvas,
     *        (xCenterOfCanvas, yCenterOfCanvas)
     */
    private double calculatePotentialYPosition (double theta, int rTotal) {
        return myCanvasHeight - (myYCenterOfCanvas + rTotal * Math.sin(theta));
    }

    /**
     * Creates a Bubble with radius r centered at (x,y).
     * 
     * @param x x position of the Bubble to be created
     * @param y y position of the Bubble to be created
     * @param w width of the object
     * @param h height of the object
     * @param name label for Element associated with (or represented by) the
     *        Bubble
     * @param label label for Element associated with bubble
     */
    public void makeElementVisual (int x, int y, int w, int h, String name,
            String label) {
        myBubbles.add(new Bubble(x, y, w, name, label));
    }

    /**
     * Bubble Drawer is informed of the JPanel's dimensions and stores the
     * values.
     * 
     * @param w width of canvas
     * @param h height of canvas
     */
    public void setCanvasSize (int w, int h) {
        myCanvasWidth = w;
        myCanvasHeight = h;
        setMaxRadius();
        setCenterOfCanvas();
    }

    /**
     * Calculates and stores the radius of the largest circle that can be
     * inscribed in the canvas.
     */

    private void setMaxRadius () {
        myMaxRadius = Math.min(myCanvasWidth, myCanvasHeight) / 2;
        Double temp = DR_PERCENT_OF_MAX_RADIUS * myMaxRadius;
        int drProportional = temp.intValue();
        myDR = Math.min(myDR, drProportional);
    }

    /**
     * Calculates and stores the center position of the canvas.
     */
    private void setCenterOfCanvas () {
        myXCenterOfCanvas = myCanvasWidth / 2;
        myYCenterOfCanvas = myCanvasHeight / 2;
    }

    /*
     * //This is for testing whether calculateElementVisualPosition places
     * Bubbles correctly
     * public static void main(String[] args){
     * myDrawBubble = new DrawBubble();
     * myDrawBubble.setCanvasSize(500,500);
     * ArrayList<Element> testArray = new ArrayList<Element>();
     * for (int k = 0; k<100; k++){
     * List<Double> temp = new ArrayList<Double>();
     * temp.add(k+1.0);
     * testArray.add(new Element("Stock "+k, 0, temp));
     * }
     * double sumValue = 0;
     * for (Element e : testArray) {
     * sumValue += e.getValue();
     * }
     * for (Element e : testArray) {
     * myDrawBubble.calculateElementVisualPosition(
     * e.getValue() / sumValue, e.getName());
     * }
     * }
     */
}
